(for Internet Explorer)
(src)
Sub  new_ObjectsFromStream( out_Objs as array of object, ClassName as string, Stream as variant )
ストリームから、オブジェクトをいくつか生成して、配列に格納します。
【引数】
out_Objs
ClassName
(出力) ストリームから生成したオブジェクトが入った配列
生成するオブジェクトのクラス名
Stream
XML ストリーム
1つもオブジェクトを生成しなかったときでも、out_Objs には配列が格納されます。
ClassName, Stream については、
テスト
→ T_ObjectFromStream フォルダ
Class  NameOnlyClass
    Public  Name
    Public  Delegate
End Class
Name 変数(任意の型の識別値)と、Delegate 変数(指しているオブジェクトなど)の
対応関係を表すクラスです。
Delegate 変数を使わずに、Name だけのオブジェクトとして使うこともできます。
(src)
関連
に対応しています。
Function  new_NameOnlyClass( in_Name as string, in_Delegate as variant ) as NameOnlyClass
NameOnlyClass のオブジェクトを生成します。
(src)
Class  JoinedClass
  Public  Left
  Public  Right
End Class
(src)
配列番号 0 と 1 で対応関係を表すことができます
→ PointerPairClass (clib)
関連
Left 変数の値と Right 変数の値の対応関係を表すクラスです。
Function  new_JoinedClass( Left as variant, Right as variant ) as JoinedClass
JoinedClass のオブジェクトを生成します。
【引数】
Left
Right
生成するオブジェクトの Left プロパティに設定する値
生成するオブジェクトの Right プロパティに設定する値
返り値
生成された JoinedClass のオブジェクト
ソース
→ vbslib.vbs
オブジェクトに数値の ID を割り当て、ID からオブジェクトが取得できるようにします。
    Set root = new SampleRootClass
    Set element = new SampleElementClass

    '// Link
    element.RootObjectID = root.ObjectID
    Set root.Element = element

    '// Call a method
    element.Method


Class  SampleRootClass
    Public  ObjectID
    Public  Element

    Private Sub  Class_Initialize()
        Me.ObjectID = g_ObjectIDs.Add( Me )
    End Sub

    Private Sub  Class_Terminate()
        g_ObjectIDs.Remove  Me.ObjectID
    End Sub
End Class

Class  SampleElementClass
    Public  RootObjectID

    Public Sub  Method()
        Set root = g_ObjectIDs( Me.RootObjectID )
    End Sub
End Class
サンプル
が期待通りに動作するようになります。
SampleRootClass
SampleElementClass
Element メンバー
RootObjectID メンバー (g_ObjectIDs 経由)
g_ObjectIDs
g_ObjectIDs
g_ObjectIDs
→ vbslib.vbs
ソース
.Add
.Remove
.Item
新規 ID を取得します
ID を返却します
Add できる最大数、同時に存在できる ID の数は 0x7FFE です。
同時に存在できる ID の数を超えて、Remove と Add を繰り返すと、返却された ID を再利用します。
同じオブジェクトに複数の ID が割り当てられる状況になっても、エラーにはなりません。
渡された ID に対応するオブジェクトを返します
関連
相互参照しているオブジェクトに対して、適切に
p
p
ユーザーの変数
ユーザーの変数
所属オブジェクトのメンバー変数やメソッドにアクセスするときは、ハンドルのメンバー変数 p を
介する必要があります。
相互参照
関連
→ ガーベージコレクション (GC)
参考
一般に、関数が終了したときにオブジェクトを参照している変数がなくなると、ガーベージコレクター
(1)
(2)
(3)
(4)
(5)
(7)
(6)
内部で相互参照するグループに LifeGroupClass を使うと下記の図のような構成になります。
グループに所属しているオブジェクトの Class_Terminate が呼び出されるタイミングは、
グループに所属しているオブジェクト(ルート オブジェクトも含む)を参照するすべてのハンドルが、
すべてのユーザーの変数から参照されなくなったときです。
( ) の中の数字は、削除される順番です。
(1) と (3)、(2) と (4)、(5) と (7) の順番は入れ替わることがあります。
(1) だけが削除されたときは、(3)が削除されるまで、(4)以降のオブジェクトは残ります。
によって
プログラム終了時に呼ばれます。 LifeGroupClass は、相互参照しているオブジェクトでも
Class_Terminate が呼び出されるようにします。
が呼ばれますが、相互参照していると Class_Terminate は呼ばれません。
このように相互参照しているオブジェクトとハンドルのオブジェクトを分けることで、
参照している変数がなくなったことをハンドルで検出できるようにしています。
オブジェクト ID を経由することで Class_Terminate を呼び出す
A
B
相互参照
→ T_Dic.vbs
T_LifeGroupClass
→ vbslib.vbs
テスト
ソース
注意
メンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要があります。
のキーや配列番号で参照することで、相互参照が原因で Class_Terminate が呼ばれなく
なる状況を回避することができます。
root.p.Member
辞書のキーや配列番号
B
A
オブジェクトの Set
が呼ばれるようにします。
p を介する必要がないが、多少手間がかかるライフサイクル管理
Class  RootClass
    Public  p  '// as RootBodyClass : Target object of this handle

    Private Sub  Class_Initialize()
        Set group = new LifeGroupClass
        group.AddHandle  Me, new RootBodyClass
    End Sub

    Private Sub  Class_Terminate()
        If not IsEmpty( Me.p.LifeGroup.Group ) Then _
            Me.p.LifeGroup.Group.AddTerminated  Me.p
    End Sub
End Class
使用サンプル
RootBodyClass
相互参照しているオブジェクトのグループのうち、最初に生成するオブジェクトのハンドルの
クラスは、下記のように定義します。 RootBodyClass を最初に生成するオブジェクトのクラス名
に変更すること以外は、そのままにしてください。
Class  RootBodyClass
      :
    Public  LifeGroup  '// Don't write except for LifeGroupClass ■追加

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub
End Class
最初に生成するオブジェクトのクラスは、メンバー変数 LifeGroup と、
を定義してください。
定義サンプル
Set root = new RootClass
root.p.Member
と同じメンバーを持ちます。
グループのルート(下記の RootClass)は、
RootClass
RootBodyClass
Reference
p
メンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要があります。
… p の追加が必要
相互参照しているオブジェクトを使用するコードに、この変更を要求することがどうしても
できないなら、p を介すだけのメソッドやプロパティを定義します。
相互参照しているオブジェクトのグループに所属するオブジェクトのメソッドから、グループに
所属させるオブジェクトを新しく生成するときは、下記の CreateMember メソッドのように、
グループに追加します。  そうしなければ、グループが削除されるタイミングで、生成した
オブジェクトに対して DestroyReferences が呼ばれなくなります。
Class  RootBodyClass
      :
    Public  LifeGroup  '// Don't write except for LifeGroupClass

    Function  CreateMember()
        Set CreateMember = Me.LifeGroup.Group.Add( new MemberClass )
            :
    End Function
End Class
定義サンプル
MemberClass
Class  MemberClass
      :
    Public  LifeGroup  '// Don't write except for LifeGroupClass ■追加

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub
End Class
グループに所属するオブジェクトのクラスは、メンバー変数 LifeGroup と、
定義サンプル
を定義してください。
です。
CreateMember メソッドの返り値は、
使用サンプル
Set root = new RootClass
Set member = root.p.CreateMember()
MemberClass
すぐに CreateMember の返り値を参照しなくなっても、DestroyReferences がすぐに呼ば
れることはありません。 グループに所属するすべてのオブジェクトが参照されなくなった
ときに呼び出されます。
ルートのハンドルが参照されなくなったときに、グループに所属する一部のオブジェクトが
削除されても構わないときは、ハンドルの代わりに生成したオブジェクトを返しても構いま
せん。
相互参照しているオブジェクトを出力するときは、下記の SearchMember メソッドのように、
を生成して返してください。
Class  RootBodyClass
      :
    Public  LifeGroup  '// Don't write except for LifeGroupClass

    Function  GetReference()
        Set GetReference = Me.LifeGroup.Group.Add( Me.Reference )
    End Function
End Class
定義サンプル
使用サンプル
Set root = new RootClass
Set member = root.p.GetReference()
を呼び出し、
GetReference
ただし、グループの中で参照するときは、LifeHandleClass を生成しないでください。
そうしなければ、ルートのハンドルが削除されるタイミングで、グループに所属する一部の
オブジェクトが削除されてしまいます。 ルートのハンドルが参照されなくなったら、グループ
に所属する一部のオブジェクトが削除されても構わないときは、LifeHandleClass を生成
しなくても構いません。
Sub  (グループのに所属するオブジェクトのクラス)::DestroyReferences()
相互参照を切ります。
グループのユーザーは、本メソッドを直接呼び出さないでください。
Class_Terminate メソッド(デストラクター)が呼び出されて、オブジェクトが
破棄されるように、相互参照を切ってください。 つまり、相互に参照している
関係にあるメンバー変数に Empty や Nothing を代入してください。
から呼ばれます。
Class  RootBodyClass
      :
    Function  DestroyReferences()
        Me.Member = Empty
    End Function
End Class
定義サンプル
(グループ)に所属する、相互参照しているオブジェクトを参照できるハンドル。
LifeHandleClass が参照している、相互参照しているオブジェクトを、グループの外にある変数
に直接格納しないでください。 もしそうしてしまうと、すべてのハンドルへの参照が無くなったときに、
相互参照が切れたオブジェクトになり、正しく動作しなくなります。
メンバー
生成
相互参照しているオブジェクトを参照します。
を呼び出します。
オブジェクトのメンバー変数やメソッドにアクセスするときは、メンバー変数 p を介する必要が
あります。
Set root = new RootClass
Set member = root.p.CreateMember()  '// member as LifeHandleClass
member.p.Method
サンプル
Dim  LifeHandleClass::p as Object
グループに所属している相互参照しているオブジェクトを参照します。
参考
オブジェクトのメンバー変数やメソッドにアクセスするときは、ハンドルのメンバー変数 p を
介する必要があります。  このように相互参照しているオブジェクトとハンドルのオブジェクトを
分けることで、参照している変数がなくなったことをハンドルで検出できるようにしています。
Sub  LifeHandleClass::Class_Terminate()
ハンドルが参照されなくなったときに、
を呼び出します。
オブジェクトをグループに追加し、ハンドルからオブジェクトを参照できるようにします
オブジェクトをグループに追加して、そのオブジェクトを参照するハンドルを生成します
オブジェクトをグループから除外します
ハンドルから参照されるカウンターを減らし、必要なら相互参照を切ります
Sub  LifeGroupClass::AddHandle( Handle as Object, Object as Object )
オブジェクトをグループに追加し、ハンドルからオブジェクトを参照できるようにします。
【引数】
Handle
(ルートの)ハンドル
ハンドルが参照しようとしているオブジェクト
Object
Function  LifeGroupClass::Add( Object as Object ) as LifeHandleClass
オブジェクトをグループに追加して、そのオブジェクトを参照するハンドルを生成して返します。
【引数】
Object
ハンドルが参照しようとしているオブジェクト
返り値
生成されたハンドル
Sub  LifeGroupClass::Remove( Object as Object )
オブジェクトをグループから除外します。
【引数】
Object
ハンドルが参照しているオブジェクト
Sub  LifeGroupClass::AddTerminated( Object as Object )
ハンドルから参照されるカウンターを減らし、必要なら相互参照を切ります。
【引数】
Object
参照されなくなったハンドルが参照しているオブジェクト
相互参照を切ることで、グループに所属するすべてのオブジェクトが削除されます。
自分(Me)も削除されます。
グループに所属するオブジェクトを参照するハンドルが他にもあるときは、
Object を参照しているハンドルだけが削除され、Object は残ります。
参考
グループのユーザーは、本メソッドを直接呼び出さないでください。
から呼ばれます。
→ T_Dic.vbs
T_DestroyerClass
→ vbslib.vbs
テスト
ソース
相互参照しているオブジェクトを削除するタイミングを制御するオブジェクト。
DestroyerClass のオブジェクトが削除されるときに、相互参照を切るメソッド DestroyReferences
を呼び出し、相互参照していたオブジェクトの Class_Terminate が呼び出されます。
Case1
WScript.echo  "End of Main"
Case2
WScript.echo  "End of Main"
WScript.echo  "もし、ここ以降でデストラクターが動くときは、"+_
    "プログラム終了時にすべてのオブジェクトを削除する"+_
    "ことによるデストラクター呼び出しです。"

Sub  Case1()
    WScript.echo  vbCRLF +"Case1:"
    Set destroyer = new DestroyerClass
    Set a_object = new MutualClass : a_object.Name = "a" : destroyer.Add  a_object
    Set b_object = new MutualClass : b_object.Name = "b" : destroyer.Add  b_object
    Set a_object.Reference = b_object
    Set b_object.Reference = a_object
End Sub

Sub  Case2()
    WScript.echo  vbCRLF +"Case2:"
    Set destroyer = new DestroyerClass
    Set a_object = new MutualClass : a_object.Name = "a"
    Set b_object = new MutualClass : b_object.Name = "b"
    Set a_object.Reference = b_object
    Set b_object.Reference = a_object
End Sub

Class  MutualClass
    Public  Name
    Public  Reference
    Public  IsDestroyer    '// ■追加

    Private Sub  Class_Terminate()
        WScript.echo  Me.Name +".Class_Terminate"
        CheckUnderDestroyer  Me  '// ■追加
    End Sub

    Public Sub  DestroyReferences()  '// ■追加
        Me.Reference = Empty
    End Sub
End Class
サンプル
Destroyer
A
B
相互参照を切る
関数が終了したときにオブジェクトを参照している変数がなくなると、ガーベージコレクターによって
Class_Terminate が呼ばれますが、相互参照していると Class_Terminate は呼ばれません。
プログラム終了時に呼ばれます。 DestroyerClass は、相互参照しているオブジェクトでも
Class_Terminate が呼ばれるようにします。
相互参照するクラスは、相互参照を切る DestroyReferences メソッドと、DestroyerClass に
登録されると True になる IsDestroyer 属性(プロパティ)を実装してください。 下記サンプルの
MutualClass を参照。
参考
→ ガーベージコレクション (GC)
DestroyerClass
Case1:
Case1: a.Class_Terminate
Case1: b.Class_Terminate
Case2:
End of Main
もし、ここ以降でデストラクターが動くときは、プログラム終了時にすべての
オブジェクトを削除することによるデストラクター呼び出しです。
Case2: a.Class_Terminate
Case2: b.Class_Terminate
表示例:
関連
の使用を推奨します。
注意
DestroyerClass の代わりに
Sub  DestroyerClass::Add( Object as variant )
相互参照するオブジェクトを登録します。
【引数】
Object
登録するオブジェクト
本関数は、Object の IsDestroyer 属性(プロパティ)を True に設定します。
DestroyerClass が削除されるタイミングで、Object の DestroyReferences メソッドが
呼ばれます。
Sub  DestroyerClass::Remove( Object as variant )
登録から除外します。
【引数】
Object
除外するオブジェクト
DestroyerClass が削除されるときよりも早く削除するオブジェクトを指定してください。
本関数は、Object の IsDestroyer 属性(プロパティ)を False に設定します。
Sub  CheckUnderDestroyer( Object as variant )
Destroyer によって管理されていなければ、エラーにします。